package com.cysyz.mylib.mylibtools.httpclient.impl;


import com.cysyz.mylib.mylibtools.httpclient.HttpRequestProxyException;
import com.cysyz.mylib.mylibtools.httpclient.HttpRequestProxy;
import com.cysyz.mylib.utils.MyMapUtils;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class HttpRequestProxyImpl implements HttpRequestProxy {

    //private final static Logger logger = LoggerFactory.getLogger(HTTPUtility.class);

    private static final int REQUEST_TIMEOUT = 30 * 1000; // 设置请求超时10秒钟
    private static final int TIMEOUT = 60 * 1000; // 连接超时时间
    private static final int SO_TIMEOUT = 60 * 1000; // 数据传输超时

    private static final Charset CHARSET = StandardCharsets.UTF_8;
    private static CloseableHttpClient client;

    static {
        // 使用线程安全的连接管理来创建HttpClient
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // Increase max total connection to 200
        cm.setMaxTotal(200);
        // Increase default max connection per route to 20
        cm.setDefaultMaxPerRoute(20);
        // Increase max connections for localhost:80 to 50
        HttpHost localhost = new HttpHost("locahost", 80);
        cm.setMaxPerRoute(new HttpRoute(localhost), 50);

        setConnectionConfig(cm);

        ConnectionKeepAliveStrategy myStrategy = setKeepAliveStratgy();


        client = HttpClients.custom()
                .setConnectionManager(cm)
                .setKeepAliveStrategy(myStrategy)
                .build();

        //设置清理连接池中空闲链接的线程
        new IdleConnectionMonitorThread(cm).start();
    }

    private static ConnectionKeepAliveStrategy setKeepAliveStratgy() {
        /*配置KeepAlive策略*/
        return new ConnectionKeepAliveStrategy() {

            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                // Honor 'keep-alive' header
                HeaderElementIterator it = new BasicHeaderElementIterator(
                        response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && param.equalsIgnoreCase("timeout")) {
                        try {
                            return Long.parseLong(value) * 1000;
                        } catch (NumberFormatException ignore) {
                            throw new HttpRequestProxyException("");
                        }
                    }
                }
                HttpHost target = (HttpHost) context.getAttribute(
                        HttpClientContext.HTTP_TARGET_HOST);
                if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
                    // Keep alive for 5 seconds only
                    return 5 * 1000;
                } else {
                    // otherwise keep alive for 30 seconds
                    return 30 * 1000;
                }
            }

        };
    }

    private static void setConnectionConfig(PoolingHttpClientConnectionManager cm) {
        /*配置ConnectionConfig*/
        ConnectionConfig.Builder connBuilder = ConnectionConfig.custom();
        connBuilder.setCharset(CHARSET);
        ConnectionConfig connConfig = connBuilder.build();

        cm.setDefaultConnectionConfig(connConfig);

		/*配置ConnectionConfig*/
        SocketConfig.Builder socketBuilder = SocketConfig.custom();
        socketBuilder.setSoTimeout(4000);

        SocketConfig socketConfig = socketBuilder.build();
        cm.setDefaultSocketConfig(socketConfig);
    }

    private CloseableHttpClient getHttpClient() {
        return client;
    }





    @Override
    public byte[] doGet(String url, Map<String, String> header) {
        return this.doRequest(url, null, header, HttpMethodType.GET, new HttpClientContext());
    }

    @Override
    public byte[] doGet(String url) {
        return this.doRequest(url, null, null, HttpMethodType.GET, new HttpClientContext());
    }

    @Override
    public byte[] doPost(String url, Map<String, String> postData) {
        return this.doRequest(url, postData, null, HttpMethodType.POST, new HttpClientContext());
    }

    @Override
    public byte[] doPost(String url, Map<String, String> postData, Map<String, String> header) {
        return this.doRequest(url, postData, header, HttpMethodType.POST, new HttpClientContext());
    }

    @Override
    public byte[] doPost(String url, Map<String, String> postData, HttpClientContext context) {
        return this.doRequest(url, postData, null, HttpMethodType.POST, context);
    }

    @Override
    public byte[] doPost(String url, Map<String, String> postData, Map<String, String> header, HttpClientContext context) {
        return this.doRequest(url, postData, header, HttpMethodType.POST, context);
    }

    private byte[] doRequest(String url, Map<String, String> postData, Map<String, String> header, HttpMethodType httpMethod, HttpClientContext context) {

        CloseableHttpClient httpClient = getHttpClient();

        HttpUriRequest request = null;

        if (HttpMethodType.GET == httpMethod) {
            request = new HttpGet(url);
        } else if (HttpMethodType.POST == httpMethod) {
            request = new HttpPost(url);
            setPostParams(postData, (HttpPost) request);
        }

        //配置请求头
        setHeaders(header, request);

        CloseableHttpResponse response = null;
        InputStream is = null;
        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
        try {

            //TODO While HttpClient instances are thread safe and can be shared between multiple threads of execution,
            //TODO it is highly recommended that each thread maintains its own dedicated instance of HttpContext .
            response = httpClient.execute(request, context);

            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            HttpEntity entity = response.getEntity();
            if (statusCode == HttpStatus.SC_OK) {
                entity.writeTo(arrayOutputStream);
            } else {
                throw new HttpRequestProxyException("request failed:[statusCode=" + statusCode + "]");
            }
            EntityUtils.consume(entity);
        } catch (IOException e) {
            throw new HttpRequestProxyException(e);
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    throw new HttpRequestProxyException(e);
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    throw new HttpRequestProxyException(e);
                }
            }
        }

        return arrayOutputStream.toByteArray();
    }

    private void setHeaders(Map<String, String> header, HttpUriRequest request) {
        // 头部请求信息
        Header[] headers = null;
        if (header != null) {
            Set<Map.Entry<String, String>> entrySet = header.entrySet();
            int dataLength = entrySet.size();
            headers = new Header[dataLength];
            int i = 0;
            for (Iterator<Map.Entry<String, String>> itor = entrySet.iterator(); itor.hasNext(); ) {
                Map.Entry entry = (Map.Entry) itor.next();
                headers[i++] = new BasicHeader(entry.getKey().toString(), entry
                        .getValue().toString());
            }
        }

        if (headers != null) {
            for (int i = 0; i < headers.length; i++) {
                request.addHeader(headers[i]);
            }
        }
    }

    private void setPostParams(Map<String, String> postData, HttpPost request) {
        if (MyMapUtils.isNotEmpty(postData)) {
            List<NameValuePair> datas = new ArrayList<NameValuePair>();
            for (String key : postData.keySet()) {
                datas.add(new BasicNameValuePair(key, postData.get(key)));
            }

            request.setEntity(new UrlEncodedFormEntity(datas, HttpRequestProxyImpl.CHARSET));

        }
    }

    public static class IdleConnectionMonitorThread extends Thread {

        private final HttpClientConnectionManager connMgr;
        private volatile boolean shutdown;

        public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
            super();
            this.connMgr = connMgr;
        }

        @Override
        public void run() {
            try {
                while (!shutdown) {
                    synchronized (this) {
                        wait(5000);
                        // Close expired connections
                        connMgr.closeExpiredConnections();
                        // Optionally, close connections
                        // that have been idle longer than 30 sec
                        connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
                    }
                }
            } catch (InterruptedException ex) {
                // terminate
            }
        }

        public void shutdown() {
            shutdown = true;
            synchronized (this) {
                notifyAll();
            }
        }

    }




	/*private String executeMethod(HttpMethod request, String encoding)
			throws HttpRequestProxyException {
		String responseContent = null;
		InputStream responseStream = null;
		BufferedReader rd = null;
		try {
			this.getHttpClient().executeMethod(request);
			if (encoding != null && !encoding.trim().isEmpty()) {
				responseStream = request.getResponseBodyAsStream();
				rd = new BufferedReader(new InputStreamReader(responseStream,
						encoding));
				String tempLine = rd.readLine();
				StringBuffer tempStr = new StringBuffer();
				while (tempLine != null) {
					tempStr.append(tempLine);
					//tempStr.append(crlf);
					tempLine = rd.readLine();
				}
				responseContent = tempStr.toString();
			} else
				responseContent = request.getResponseBodyAsString();

			Header locationHeader = request.getResponseHeader("location");
			// 返回代码为302,301时，表示页面己经重定向，则重新请求location的url，这在
			// 一些登录授权取cookie时很重要
			if (locationHeader != null) {
				String redirectUrl = locationHeader.getValue();
				this.doRequest(redirectUrl, null, null, "");
			}
		} catch (HttpRequestProxyException e) {
			throw new HttpRequestProxyException(e.getMessage());
		} catch (IOException e) {
			throw new HttpRequestProxyException(e.getMessage());

		} finally {
			if (rd != null)
				try {
					rd.close();
				} catch (IOException e) {
					throw new HttpRequestProxyException(e.getMessage());
				}
			if (responseStream != null)
				try {
					responseStream.close();
				} catch (IOException e) {
					throw new HttpRequestProxyException(e.getMessage());

				}
		}
		return responseContent;
	}*/

	/*@Override
	public String doSpecialRequest(String url, int count, String encoding)
			throws HttpRequestProxyException {
		String str = null;
		InputStream responseStream = null;
		BufferedReader rd = null;
		GetMethod getRequest = new GetMethod(url);
		// 关闭httpclient自动重定向动能
		getRequest.setFollowRedirects(false);
		try {

			this.client.executeMethod(getRequest);
			Header header = getRequest.getResponseHeader("location");
			if (header != null) {
				// 请求重定向后的ＵＲＬ，count同时加1
				this.doSpecialRequest(header.getValue(), count + 1, encoding);
			}
			// 这里用count作为标志位，当count为0时才返回请求的ＵＲＬ文本,
			// 这样就可以忽略所有的递归重定向时返回文本流操作，提高性能
			if (count == 0) {
				getRequest = new GetMethod(url);
				getRequest.setFollowRedirects(false);
				this.client.executeMethod(getRequest);
				responseStream = getRequest.getResponseBodyAsStream();
				rd = new BufferedReader(new InputStreamReader(responseStream,
						encoding));
				String tempLine = rd.readLine();
				StringBuffer tempStr = new StringBuffer();
				String crlf = System.getProperty("line.separator");
				while (tempLine != null) {
					tempStr.append(tempLine);
					tempStr.append(crlf);
					tempLine = rd.readLine();
				}
				str = tempStr.toString();
			}

		} catch (HttpRequestProxyException e) {
			throw new HttpRequestProxyException(e.getMessage());
		} catch (IOException e) {
			throw new HttpRequestProxyException(e.getMessage());
		} finally {
			getRequest.releaseConnection();
			if (rd != null)
				try {
					rd.close();
				} catch (IOException e) {
					throw new HttpRequestProxyException(e.getMessage());
				}
			if (responseStream != null)
				try {
					responseStream.close();
				} catch (IOException e) {
					throw new HttpRequestProxyException(e.getMessage());
				}
		}
		return str;
	}*/

	/*public static void main(String[] args) throws Exception {
		HttpRequestProxy hrp = new HttpRequestProxyImpl();
		Map header = new HashMap();
		header.put(
				"User-Agent",
				"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 1.7; .NET CLR 1.1.4322; CIBA; .NET CLR 2.0.50727)");
		String str = hrp
				.doRequest(
						"http://www.cma-cgm.com/en/eBusiness/Tracking/Default.aspx?BolNumber=GZ2108827",
						null, header, null);
		System.out.println(str.contains("row_CRXU1587647"));
		// System.out.println(str);
	}*/

}
